#include <WiFi.h>
#include <HTTPClient.h>
#include <WebServer.h>
#include "esp_camera.h"

#define STREAM_RESOLUTION FRAMESIZE_QVGA  // FRAMESIZE_VGA 640×480 FRAMESIZE_QVGA    // 320x240 (流畅优先)
#define STREAM_QUALITY 10                   // JPEG质量 (10-12平衡)
#define TARGET_FPS 15                       // 目标帧率
#define UPLOAD_INTERVAL 10000               // 上传间隔(10秒)
#define WIFI_CHANNEL 6   
// 函数声明
void handleRoot();
void handleStream();
void handleCapture();
void handleControl();
void sendFrameToServer();
void handleStreamFrame();
void checkWiFiConnection();
void optimizeForQuality();

// WiFi配置
const char* ssid = "CMCC-fp9k";
const char* password = "Wxz9au<6";

// 服务器配置
const char* serverUrl = "http://101.43.112.148:4000/upload";

// 本地视频流服务器
WebServer server(80);

// 摄像头引脚配置
#define PWDN_GPIO_NUM     32
#define RESET_GPIO_NUM    -1
#define XCLK_GPIO_NUM      0
#define SIOD_GPIO_NUM     26
#define SIOC_GPIO_NUM     27
#define Y9_GPIO_NUM       35
#define Y8_GPIO_NUM       34
#define Y7_GPIO_NUM       39
#define Y6_GPIO_NUM       36
#define Y5_GPIO_NUM       21
#define Y4_GPIO_NUM       19
#define Y3_GPIO_NUM       18
#define Y2_GPIO_NUM        5
#define VSYNC_GPIO_NUM    25
#define HREF_GPIO_NUM     23
#define PCLK_GPIO_NUM     22

// 视频流控制变量
bool streamEnabled = false;
uint32_t lastFrameTime = 0;
uint32_t lastRemoteSend = 0;
const uint32_t remoteSendInterval = 100;  // 10 FPS (100ms)
WiFiClient streamClient; // 用于保持流客户端连接

// 性能统计
uint32_t frameCount = 0;
uint32_t startTime = 0;
float averageFPS = 0;
int jpegQuality = 8; // 最高质量设置 (10-63)
int resolutionLevel = 0; // 0: QQVGA, 1: QVGA, 2: SVGA

// 根页面处理
void handleRoot() {
  String html = "<html><head><title>ESP32-CAM HD Stream</title>";
  html += "<meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">";
  html += "<style>";
  html += "body { font-family: Arial, sans-serif; text-align: center; margin: 20px; }";
  html += ".container { max-width: 800px; margin: 0 auto; }";
  html += "button { padding: 10px 20px; font-size: 16px; margin: 10px; }";
  html += "#video { max-width: 100%; margin-top: 20px; border: 1px solid #ddd; }";
  html += "#stats { margin-top: 20px; font-size: 14px; background: #f8f8f8; padding: 15px; border-radius: 8px; }";
  html += ".quality-control { margin: 20px 0; }";
  html += ".btn { background: #4CAF50; color: white; border: none; cursor: pointer; transition: background 0.3s; }";
  html += ".btn:hover { background: #45a049; }";
  html += ".btn-quality { padding: 8px 15px; font-size: 14px; }";
  html += "</style>";
  html += "<script>";
  html += "function toggleStream() {";
  html += "  var btn = document.getElementById('toggleBtn');";
  html += "  var isStreaming = btn.textContent.includes('Stop');";
  html += "  fetch('/control?cmd=' + (isStreaming ? 'stop' : 'start'))";
  html += "    .then(response => response.text())";
  html += "    .then(data => {";
  html += "      if (data === 'OK') {";
  html += "        btn.textContent = isStreaming ? 'Start Stream' : 'Stop Stream';";
  html += "        btn.className = isStreaming ? 'btn btn-start' : 'btn btn-stop';";
  html += "        document.getElementById('video').src = isStreaming ? '' : '/stream';";
  html += "      }";
  html += "    });";
  html += "}";
  
  // 质量调整功能
  html += "function adjustQuality(change) {";
  html += "  fetch('/quality?change=' + change)";
  html += "    .then(response => response.text())";
  html += "    .then(data => {";
  html += "      document.getElementById('qualityValue').textContent = data;";
  html += "    });";
  html += "}";
  
  html += "</script>";
  html += "</head><body>";
  html += "<div class=\"container\">";
  html += "<h1>ESP32-CAM HD Stream</h1>";
  html += "<p>High quality with smooth streaming</p>";
  html += "<button id=\"toggleBtn\" class=\"btn\" onclick=\"toggleStream()\">Start Stream</button>";
  html += "<button class=\"btn\" onclick=\"location.href='/capture'\">Capture Image</button>";
  
  // 质量控制
  html += "<div class=\"quality-control\">";
  html += "<button class=\"btn btn-quality\" onclick=\"adjustQuality(-1)\">- Quality</button>";
  html += "<span> JPEG Quality: <span id=\"qualityValue\" style=\"font-weight:bold;\">" + String(jpegQuality) + "</span></span>";
  html += "<button class=\"btn btn-quality\" onclick=\"adjustQuality(1)\">+ Quality</button>";
  html += "</div>";
  
  html += "<div><img id=\"video\" src=\"\"></div>";
  html += "<div id=\"stats\">";
  html += "<p>Stream URL: <a href=\"/stream\">/stream</a></p>";
  html += "<p>IP: " + WiFi.localIP().toString() + "</p>";
  html += "<p>FPS: <span id=\"fps\">0</span></p>";
  html += "<p>Frame Size: <span id=\"size\">0</span> bytes</p>";
  html += "<p>Resolution: <span id=\"res\">SVGA (800x600)</span></p>";
  html += "<p>Quality Level: <span id=\"ql\">" + String(jpegQuality) + "</span></p>";
  html += "</div>";
  html += "<script>";
  html += "setInterval(() => {";
  html += "  fetch('/stats').then(r => r.json()).then(data => {";
  html += "    document.getElementById('fps').textContent = data.fps.toFixed(1);";
  html += "    document.getElementById('size').textContent = data.size;";
  html += "    document.getElementById('res').textContent = data.res;";
  html += "    document.getElementById('ql').textContent = data.quality;";
  html += "  });";
  html += "}, 1000);";
  html += "</script>";
  html += "</div></body></html>";
  
  server.send(200, "text/html", html);
}

// 视频流处理 - 非阻塞方式
void handleStream() {
  // 设置流响应头
  server.client().println("HTTP/1.1 200 OK");
  server.client().println("Content-Type: multipart/x-mixed-replace; boundary=frame");
  server.client().println("Access-Control-Allow-Origin: *");
  server.client().println();
  
  // 保存客户端引用
  streamClient = server.client();
  streamEnabled = true;
  lastFrameTime = millis();
  
  Serial.println("HD stream started");
}

// 处理流帧 - 从loop()调用
void handleStreamFrame() {
  if (!streamEnabled || !streamClient.connected()) {
    if (streamEnabled) {
      Serial.println("Stream client disconnected");
      streamEnabled = false;
    }
    return;
  }
  
  uint32_t currentTime = millis();
  if (currentTime - lastFrameTime >= remoteSendInterval) {
    lastFrameTime = currentTime;
    
    camera_fb_t *fb = esp_camera_fb_get();
    if (!fb) {
      Serial.println("Camera capture failed");
      return;
    }
    
    // 发送图像帧
    streamClient.println("--frame");
    streamClient.println("Content-Type: image/jpeg");
    streamClient.println("Content-Length: " + String(fb->len));
    streamClient.println();
    streamClient.write(fb->buf, fb->len);
    streamClient.println();
    
    // 释放帧缓冲区
    esp_camera_fb_return(fb);
  }
}

// 单帧捕获处理
void handleCapture() {
  camera_fb_t *fb = esp_camera_fb_get();
  if (!fb) {
    server.send(500, "text/plain", "Camera capture failed");
    return;
  }
  
  server.sendHeader("Content-Type", "image/jpeg");
  server.sendHeader("Content-Disposition", "inline; filename=capture.jpg");
  server.sendHeader("Access-Control-Allow-Origin", "*");
  server.send_P(200, "image/jpeg", (const char*)fb->buf, fb->len);
  
  esp_camera_fb_return(fb);
}

// 流控制处理
void handleControl() {
  if (server.hasArg("cmd")) {
    String cmd = server.arg("cmd");
    if (cmd == "start") {
      streamEnabled = true;
      server.send(200, "text/plain", "OK");
    } else if (cmd == "stop") {
      streamEnabled = false;
      server.send(200, "text/plain", "OK");
    } else {
      server.send(400, "text/plain", "Invalid command");
    }
  } else {
    server.send(400, "text/plain", "Missing command");
  }
}

// 质量调整处理
void handleQuality() {
  if (server.hasArg("change")) {
    int change = server.arg("change").toInt();
    jpegQuality += change;
    
    // 扩展质量范围 (10-63)
    jpegQuality = constrain(jpegQuality, 10, 63);
    
    // 应用新质量设置
    sensor_t *s = esp_camera_sensor_get();
    s->set_quality(s, jpegQuality);
    
    server.send(200, "text/plain", String(jpegQuality));
  } else {
    server.send(400, "text/plain", "Missing change parameter");
  }
}

// 性能统计
void handleStats() {
  String json = "{";
  json += "\"fps\":" + String(averageFPS, 1) + ",";
  json += "\"size\":";
  
  // 获取最后一帧大小
  camera_fb_t *fb = esp_camera_fb_get();
  if (fb) {
    json += String(fb->len);
    esp_camera_fb_return(fb);
  } else {
    json += "0";
  }
  
  json += ",\"res\":\"";
  
  // 分辨率描述
  switch(resolutionLevel) {
    case 0: json += "QQVGA (160x120)"; break;
    case 1: json += "QVGA (320x240)"; break;
    case 2: json += "SVGA (800x600)"; break;  // 更新分辨率描述
    default: json += "Custom"; break;
  }
  
  json += "\",\"quality\":" + String(jpegQuality) + "}";
  
  server.send(200, "application/json", json);
}

// 发送帧到服务器
void sendFrameToServer() {
  if (WiFi.status() != WL_CONNECTED) {
    Serial.println("WiFi not connected. Skipping upload.");
    return;
  }

  camera_fb_t *fb = esp_camera_fb_get();
  if (!fb) {
    Serial.println("Camera capture failed for remote send");
    return;
  }

  // 性能统计
  frameCount++;
  if (millis() - startTime >= 1000) {
    averageFPS = frameCount * 1000.0 / (millis() - startTime);
    Serial.printf("FPS: %.1f, Size: %d bytes, Quality: %d, Res: %d\n", 
                 averageFPS, fb->len, jpegQuality, resolutionLevel);
    frameCount = 0;
    startTime = millis();
    
    // 根据性能优化质量
    optimizeForQuality();
  }
  
  // 发送帧到服务器 (非阻塞)
  HTTPClient http;
  http.begin(serverUrl);
  http.addHeader("Content-Type", "image/jpeg");
  http.addHeader("x-device-id", "esp32cam-hd");
  
  // 异步发送，不等待响应
  http.POST(fb->buf, fb->len);
  http.end();
  
  esp_camera_fb_return(fb);
}

// 根据性能优化画质
void optimizeForQuality() {
  static uint32_t lastOptimization = 0;
  if (millis() - lastOptimization < 5000) return; // 每5秒检查一次
  lastOptimization = millis();
  
  sensor_t *s = esp_camera_sensor_get();
  
  // 优先保证8FPS，在此前提下提高画质
  if (averageFPS > 8.0 && jpegQuality < 63) {
    // 帧率充足，提高质量
    jpegQuality = min(jpegQuality + 1, 63);
    s->set_quality(s, jpegQuality);
    Serial.printf("Increasing quality to %d\n", jpegQuality);
  }
  else if (averageFPS < 5.0) {
    // 帧率过低，降低质量
    jpegQuality = max(jpegQuality - 1, 10);
    s->set_quality(s, jpegQuality);
    Serial.printf("Lowering quality to %d for better FPS\n", jpegQuality);
  }
}

// WiFi连接检查
void checkWiFiConnection() {
  static uint32_t lastCheck = 0;
  if (millis() - lastCheck > 5000) {
    lastCheck = millis();
    if (WiFi.status() != WL_CONNECTED) {
      Serial.println("WiFi disconnected. Reconnecting...");
      WiFi.disconnect();
      WiFi.begin(ssid, password);
      while (WiFi.status() != WL_CONNECTED) {
        delay(100);
        Serial.print(".");
      }
      Serial.println("\nWiFi reconnected");
    }
  }
}

void setup() {
  Serial.begin(115200);
  Serial.println("\n\nStarting ESP32-CAM HD Stream...");
  
  // 连接 WiFi
  WiFi.begin(ssid, password);
  Serial.print("Connecting to WiFi");
  while (WiFi.status() != WL_CONNECTED) {
    delay(100);
    Serial.print(".");
  }
  Serial.println("\nWiFi connected");
  Serial.print("IP Address: ");
  Serial.println(WiFi.localIP());

  camera_config_t config;
  config.pin_pwdn = PWDN_GPIO_NUM;
  config.pin_reset = RESET_GPIO_NUM;
  config.pin_xclk = XCLK_GPIO_NUM;
  config.pin_sscb_sda = SIOD_GPIO_NUM;
  config.pin_sscb_scl = SIOC_GPIO_NUM;
  config.pin_d7 = Y9_GPIO_NUM;
  config.pin_d6 = Y8_GPIO_NUM;
  config.pin_d5 = Y7_GPIO_NUM;
  config.pin_d4 = Y6_GPIO_NUM;
  config.pin_d3 = Y5_GPIO_NUM;
  config.pin_d2 = Y4_GPIO_NUM;
  config.pin_d1 = Y3_GPIO_NUM;
  config.pin_d0 = Y2_GPIO_NUM;
  config.pin_vsync = VSYNC_GPIO_NUM;
  config.pin_href = HREF_GPIO_NUM;
  config.pin_pclk = PCLK_GPIO_NUM;
  config.xclk_freq_hz = 20000000;
  config.pixel_format = PIXFORMAT_JPEG;
  
  // 关键分辨率设置
  config.frame_size = STREAM_RESOLUTION;
  config.jpeg_quality = STREAM_QUALITY;
  config.fb_count = 2; // 双缓冲

  // 初始化摄像头
  esp_err_t err = esp_camera_init(&config);
  if (err != ESP_OK) {
    Serial.printf("Camera init failed: 0x%x", err);
    while (true) delay(100); // 卡住等待解决
  }

  // 配置传感器参数
  // sensor_t *s = esp_camera_sensor_get();
  // if (s != NULL) {
  //   s->set_brightness(s, 0);     // 亮度 (-2 to 2)
  //   s->set_contrast(s, 0);       // 对比度 (-2 to 2)
  //   s->set_saturation(s, 0);     // 饱和度 (-2 to 2)
  //   s->set_special_effect(s, 0); // 特效 (0-6)
  //   s->set_whitebal(s, 1);       // 自动白平衡
  //   s->set_awb_gain(s, 1);       // 自动白平衡增益
  //   s->set_wb_mode(s, 0);        // 白平衡模式
  //   s->set_exposure_ctrl(s, 1);  // 自动曝光
  //   s->set_aec2(s, 0);           // 自动曝光控制
  //   s->set_ae_level(s, 0);       // 曝光等级
  //   s->set_aec_value(s, 300);    // 曝光值
  //   s->set_gain_ctrl(s, 1);      // 自动增益
  //   s->set_agc_gain(s, 0);       // 自动增益控制
  //   s->set_gainceiling(s, (gainceiling_t)6); // 增益上限
  //   s->set_bpc(s, 0);            // 黑点校正
  //   s->set_wpc(s, 1);            // 白点校正
  //   s->set_raw_gma(s, 1);        // Gamma校正
  //   s->set_lenc(s, 1);           // 镜头校正
  //   s->set_hmirror(s, 0);        // 水平镜像
  //   s->set_vflip(s, 0);          // 垂直翻转
  //   s->set_dcw(s, 1);            // 下采样使能
  //   s->set_colorbar(s, 0);       // 颜色条测试
  // } else {
  //   Serial.println("Failed to get camera sensor");
  // }

  // 设置 Web 服务器路由
  server.on("/", HTTP_GET, handleRoot);
  server.on("/stream", HTTP_GET, handleStream);
  server.on("/capture", HTTP_GET, handleCapture);
  server.on("/control", HTTP_GET, handleControl);
  server.on("/stats", HTTP_GET, handleStats); // 性能统计
  server.on("/quality", HTTP_GET, handleQuality); // 质量调整
  
  // 启动 Web 服务器
  server.begin();
  Serial.println("HTTP server started");
  
  // 初始化性能统计
  startTime = millis();
}

void loop() {
  server.handleClient();
  handleStreamFrame(); // 非阻塞方式处理视频流
  checkWiFiConnection();
  
  uint32_t currentTime = millis();
  
  // 每100ms发送一次到远程服务器 (10FPS)
  if (currentTime - lastRemoteSend >= remoteSendInterval) {
    lastRemoteSend = currentTime;
    sendFrameToServer();
  }
  
  // 最小延迟确保稳定性
  delay(1);
}